package org.mentalsmash.tazio.evaluation.tests;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.mentalsmash.tazio.evaluation.mock.VeryLightCounter;
import org.mentalsmash.tazio.impl.WRMTazioStarter;
import org.mentalsmash.tazio.transactions.RollbackException;
import org.mentalsmash.tazio.transactions.Share;
import org.mentalsmash.tazio.transactions.Transaction;
import org.mentalsmash.tazio.transactions.TransactionalSystemException;
import org.mentalsmash.tazio.transactions.UserTransactionManager;
import org.mentalsmash.tazio.transactions.management.TransactionStateException;

public class VeryLightCounterTest {

    public static final int MAX_COUNTER = 200;
    private static final int sleepTime = 500;
   
    private Object _lock = new Object();
    private int threadFinished = 0;

    public static void main(String[] args) {
	String conf = args[0];
	int numThreads = Integer.parseInt(args[1]);
	VeryLightCounterTest instance = new VeryLightCounterTest();
	instance.doJob(conf,numThreads, sleepTime);
    }
    
    public void doJob(String conf,int numThreads,int sleeptime) {
	
	
	System.out.println("Starting Tazio...");
	WRMTazioStarter starter = new WRMTazioStarter(false, conf);
	starter.initTazio();
	System.out.println("Tazio started.");

	TestData mbean = new TestData();
	mbean.register();
	System.out.println("TestDataMBean registered.");
	
	Share share = starter.getShare();
	UserTransactionManager txManager = starter.getTransactionManager();

	VeryLightCounter testcounter = retrieveVeryLightCounter(share);
	
	try {
	    System.out.println("Counter size is : "
		    + testcounter.getString().getBytes().length + " bytes");
	} catch (Exception ex) {
	    ex.printStackTrace();
	    //System.exit(1);
	}

	VeryLightCounterSetter setter = new VeryLightCounterSetter(txManager, testcounter,
		MAX_COUNTER,sleeptime);

	List<Thread> threads = new ArrayList<Thread>(numThreads);

	for (int i = 0; i < numThreads; i++) {
	    Thread t = new Thread(setter);
	    t.setName("t" + i);
	    threads.add(t);

	}


	System.out.println("Starting test...");
	long now = System.currentTimeMillis();
	for (Thread t : threads) {
	    t.start();
	}
	
	synchronized (_lock) {
	    while(threadFinished < numThreads) {
		try {
		    _lock.wait();
		} catch (InterruptedException e) {
		    e.printStackTrace();
		}
	    }
	}
	
	long duration = System.currentTimeMillis() - now;
	mbean.setDuration(duration);
	mbean.setFinished();
	System.out.println("Test ended ("+duration+" ms).");
	
	
    }

    public VeryLightCounter retrieveVeryLightCounter(Share share) {
	String properties ="name:test-counter";
	VeryLightCounter testcounter = share.getObject(properties);

	if (testcounter == null) {
	    try {
		testcounter = share.createObject(VeryLightCounter.class, properties);
	    } catch (TransactionStateException e1) {
		e1.printStackTrace();
		throw new RuntimeException("Cannot retrieve counter object!");
	    } catch (RollbackException e1) {
		e1.printStackTrace();
		throw new RuntimeException("Cannot retrieve counter object!");
	    }
	} else {
	    System.out.println("Object retrieved from the share");
	}
	return testcounter;
    }
    
    class VeryLightCounterSetter implements Runnable {
	    

	    private final UserTransactionManager txm;

	    private final VeryLightCounter toIncrement;

	    private final int limit;

	    private final ThreadLocal<String> name = new ThreadLocal<String>();
	    private final int sleepTime;

	    public VeryLightCounterSetter(UserTransactionManager txm, VeryLightCounter toIncrement,
		    int limiut, int sleeptime) {
		this.txm = txm;
		this.toIncrement = toIncrement;
		this.limit = limiut;
		this.sleepTime = sleeptime;
	    }

	    public void run() {
		this.name.set(Thread.currentThread().getName());
		final Random rand = new Random();
		for (;;) {
		    try {
			Transaction currentTx = txm.begin();
			
			String payload = toIncrement.getString();
			toIncrement.setString(payload);

			int currentVal = toIncrement.getValue();
			if (currentVal < this.limit) {
			    toIncrement.setValue(currentVal + 1);
			    currentTx.commit();
			    System.out.println("<" + this.name.get()
				    + "> VeryLightCounter set to " + (currentVal + 1));
			} else {
			    System.out.println("<" + this.name.get()
				    + "> VeryLightCounter to its max value");
			    currentTx.rollback();
			    break;
			}

		    } catch (RollbackException e) {
			System.err.println("<" + this.name.get()
				+ "> a transaction rolled back!");
		    } catch (TransactionStateException e) {
			e.printStackTrace();
		    } catch (TransactionalSystemException e) {
			e.printStackTrace();
		    }
		    try {
			Thread.sleep(rand.nextInt(this.sleepTime));
		    } catch (InterruptedException e) {
			e.printStackTrace();
		    }

		}
		
		synchronized (_lock) {
		    threadFinished++;
		    _lock.notifyAll();
		}

	    }
	}


}

